﻿using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel
{
    /// <summary>
    /// Implements an undoable command for setting a property of a <see cref="Model.Elements.DynamicMarkElement"/> 
    /// that is presented by a given <see cref="Elements.CWNotation.DynamicMark"/>.
    /// </summary>
    /// <remarks>
    /// Input Parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     <see cref="Property"/> (<see cref="string"/>)
    /// </term>
    /// <description>
    ///     The name of the property that will be set by this command.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Target"/> (<see cref="Elements.CWNotation.DynamicMark"/>)
    /// </term>
    /// <description>
    ///     The dynamic mark that will be modified by this command.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Value"/> (<typeparamref name="T"/>)
    /// </term>
    /// <description>
    ///     The value that will be set to <see cref="Property"/>.
    /// </description>
    /// </item>
    /// </list>
    /// </remarks>
    /// <typeparam name="T">The type of the property set by this command</typeparam>
    public class SetDynamicMarkPropertyCommand<T> : UndoableCommandBase
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SetDynamicMarkPropertyCommand{T}"/> class.
        /// </summary>
        /// <param name="undoStack">The undo command stack</param>
        /// <param name="redoStack">The redo command stack</param>
        public SetDynamicMarkPropertyCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<string>("Property", ParameterDirection.In);
            AddParameter<Elements.CWNotation.DynamicMark>("Target", ParameterDirection.In);
            AddParameter<T>("Value", ParameterDirection.In);

            Name = "Modify Dynamic Mark";
        }

        #endregion

        #region Command Logic

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            var target = (Model.Elements.DynamicMarkElement)((Model.Elements.CWNotation.DynamicMark)Target.AdaptedElement).PresentedElement;
            Model.Validation.Rule[] rules;
            
            oldValue = (T)target.GetPropertyValue(Property);
            target.ValidateAndSetPropertyValue(Property, Value, out rules);
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            var target = (Model.Elements.DynamicMarkElement)((Model.Elements.CWNotation.DynamicMark)Target.AdaptedElement).PresentedElement;
            Model.Validation.Rule[] rules;

            target.ValidateAndSetPropertyValue(Property, Value, out rules);
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            var target = (Model.Elements.DynamicMarkElement)((Model.Elements.CWNotation.DynamicMark)Target.AdaptedElement).PresentedElement;
            Model.Validation.Rule[] rules;

            target.ValidateAndSetPropertyValue(Property, oldValue, out rules);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the property that will be set by this command.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// The property that will be set by this command.<br/>
        /// This property wraps the "Property" command parameter.
        /// </para>
        /// </value>
        public string Property
        {
            get { return GetParameterValue<string>("Property"); }
            set { SetParameterValue<string>("Property", value); }
        }

        /// <summary>
        /// Gets or sets the dynamic mark that is the target of this command.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.CWNotation.DynamicMark"/>
        /// </para>
        /// <para>
        /// The dynamic mark that is the target of this command.<br/>
        /// This property wraps the "Target" command parameter.
        /// </para>
        /// </value>
        public Elements.CWNotation.DynamicMark Target
        {
            get { return GetParameterValue<Elements.CWNotation.DynamicMark>("Target"); }
            set { SetParameterValue<Elements.CWNotation.DynamicMark>("Target", value); }
        }

        /// <summary>
        /// Gets or sets the value that will be set by this command.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <typeparamref name="T"/>
        /// </para>
        /// <para>
        /// The value that will be set to the <see cref="Property"/>.<br/>
        /// This property wraps the "Value" command parameter.
        /// </para>
        /// </value>
        public T Value
        {
            get { return GetParameterValue<T>("Value"); }
            set { SetParameterValue<T>("Value", value); }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the old value of the element.
        /// </summary>
        private T oldValue;

        #endregion
    }
}
